grammar Compiler;
// [file: Compiler.g, started: 04-06-2012]
// Basic Expression Language
// Compiler.g: Lexer + Parser
// @author   Keith Davelaar
// @version  2012.06.24

options {
    k         =   1;    // LL(1) - do not use LL(*)
    output    =   AST;
    language  =   Java; 
}

tokens {  
  //-- Marks --\\
  DOT 			= '.'	;
  COMMA 		= ','	;
  SEMICOLON 	= ';'	;
  LPAREN 		= '('	;
  RPAREN 		= ')'	;
  COLON 		= ':'	;
  LCURLY 		= '{'	;
  RCURLY 		= '}'	;
  UNDERSCORE 	= '_'	;
  
  //-- Declarations --\\
  //-- Arithmetic Operators --\\
  PLUS 	= 'plus'						;
  MINUS = 'minus'						;
  DIV 	= 'divided by'					;
  MULT 	= 'multiplied by'				;
  EQ 	= 'equals'						;
  NE 	= 'doesn\'t equal'				;
  LT 	= 'is less than'				;
  LE 	= 'is smaller than or equal to'	;
  GE 	= 'is more than'				;
  GT 	= 'is larger than or equal to'	;
  MOD 	= 'modulo'						;
  AND 	= 'and'							;
  OR 	= 'or'							;
  NOT 	= 'not'							;
  
  TRUE	= 'true'						;
  FALSE = 'false'						;
  
  // Constant declaration
  CD1 = 'Constant:'			;
  CD2 = 'with value'		;
  
  //Variable declaration
  VD1 = 'Variable:'			;
  VD2 = 'of type'			;
  
  //-- Expressions --\\
  // Compound expression
  CE1 	= 'Start compound:'	;
  CE2 	= 'End compound'	;
  
  //Assignment expression
  AE1 	= 'Assign to'		;
  AE2 	= 'the value of'	;
  
  //Print expression
  PE1 	= 'Print:'			;
  PE2 	= 'End print'		;
  
  // Read expression
  RE	= 'Read:'			;

  //If/While expression
  IE 	= 'If'				;
  THEN 	= 'then'			;
  ELSE 	= 'otherwise'		;
  
  WE 	= 'While'			;
  DO 	= 'do:'				;
  
  END1 	= 'End'				;
  END2 	= 'do nothing. End'	;
    
  // Functions/Procedures
  PROC1		= 'Procedure:'						;
  PC 		= 'Perform procedure'				;
  FUNC1 	= 'Function:'						;
  FC 		= 'Perform function'				;
  PAR 		= 'with parameters'					;
  ARG 		= 'with the following arguments:'	;
  RET1 		= 'resulting in an'					;
  RET2 		= 'resulting in a'					;
  RETURN	= 'return'							;
  
  //-- Types --\\
  CHARDEF   = 'character'	;
  INTDEF    = 'integer'		;
  BOOLDEF   = 'boolean'		;
  
    //-- Program commands --\\
  BEGIN = 'Start of program.'	;
  END   = 'End of program.'     ;
}

@members {
    private List<RecognitionException> exceptions = new ArrayList<RecognitionException>();
    
    public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
        String hdr = getErrorHeader(e);
        String msg = getErrorMessage(e, tokenNames);
        RecognitionException se = new CompilerException(hdr+" "+msg);
        exceptions.add(se);
    }
    
    public List<RecognitionException> getExceptions(){ return exceptions; }
}

// {{{---Parser rules---}}} \\
program
    :	BEGIN^ statement+ END!
    ;
  
statement
    :	declaration DOT!
    |	procedure DOT!
    |	function DOT!
    |	expression DOT!
    ;

declaration
    :	CD1^ IDENTIFIER CD2! operand
    |	VD1^ IDENTIFIER VD2! type
    ;

procedure
	:	PROC1^ IDENTIFIER PAR! (parameter (SEMICOLON! parameter)*)? DOT! p_compound END1!
	;

p_compound
	:	LCURLY^ ((declaration|expression) DOT!)+ RCURLY!
	;
	
function
	:	FUNC1^ IDENTIFIER PAR! (parameter (SEMICOLON! parameter)*)? (RET1|RET2)! type DOT! f_compound END1!
	;
	
f_compound
	:	LCURLY^ ((declaration|expression) DOT!)* RETURN expression DOT! RCURLY!
	;
	
parameter
	:	IDENTIFIER COMMA! type
	;
	  
expression
    :	assignment
    |	arithmetic
    ;

assignment
    :	(AE1|AE2)^ IDENTIFIER COLON! expression
    ;
    	
arithmetic
    :	priority5 (OR^ priority5)?
    ;
    
priority5
    :	priority4 (AND^ priority4)?
    ;
    
priority4
    :	priority3 ((LT|LE|GE|GT|EQ|NE)^ priority3)?
    ;
    
priority3
    :	priority2 ((PLUS|MINUS)^ priority2)*
    ;
    
priority2
    :	priority1 ((MULT|DIV|MOD)^ priority1)*
    ;
    
priority1
    :	(PLUS|MINUS|NOT)^ operand
    |	operand
    ;

operand   
    : 	TRUE
    | 	FALSE
    | 	INTEGER
    | 	CHARACTER
    | 	IDENTIFIER
    |	LPAREN! arithmetic RPAREN!
    |	read
    | 	print
    |	compound
    | 	conditional
    | 	dowhile
    |	procCall
    |	funcCall
    ;

read  
    :	RE^ IDENTIFIER (COMMA! IDENTIFIER)*
    ;

print   
    :	PE1^ expression (SEMICOLON! expression)* DOT! PE2!
    ;
    
compound
    :  	CE1^ statement+ CE2!
    |	LCURLY^ statement+ RCURLY!
    ;

conditional   
    :	IE^ compound THEN! compound ELSE! compound? (END1|END2)!
    ;

dowhile
   	:	WE^ compound DO! compound END1!
   	;
   	
procCall
   	:	PC^ IDENTIFIER ARG! LPAREN! (expression (SEMICOLON! expression)*)? RPAREN!
   	;
	
funcCall
	:	FC^ IDENTIFIER ARG! LPAREN! (expression (SEMICOLON! expression)*)? RPAREN!
	;
	
type
    :	INTDEF
    |	CHARDEF
    |	BOOLDEF
    ;

// {{{--- Lexer rules ---}}} \\    
INTEGER
    :	DIGIT+
    ;
    
CHARACTER
    :	LETTER
    ;
	
IDENTIFIER
    :	LETTER (LETTER | DIGIT | UNDERSCORE)*
    ;

fragment	DIGIT	:	('0'..'9');
fragment	LOWER	:	('a'..'z');
fragment	UPPER	:	('A'..'Z');
fragment	LETTER	:	(LOWER | UPPER);

// {{{--- Ignored Tokens ---}}} \\
COMMENT
    :	'//' .* '\n'
    	{ $channel=HIDDEN; }
    ;

WS
    :	(' ' | '\t' | '\f' | '\r' | '\n')+
    	{ $channel=HIDDEN; }
    ;
//E{Compiler.g}